package controleur;

import java.awt.EventQueue;
import java.util.ArrayList;
import java.util.Collection;

import sim_moteur.Coordonnee;
import sim_moteur.Simulateur;
import sim_moteur.Simulateur.ChosesPossibles;
import sim_moteur.Terrain;
import sim_moteur.element.Chose;
import sim_moteur.element.Vivant;
import sim_moteur.element.vegetal.Vegetal;
import sim_moteur.factory.SimFactory;
import vue.Affichable;
import vue.AppWindow;
import vue.AppWindow.FournisseurDeChose;
import vue.XY;
import vue.event.InitEvent;
import vue.event.StartEvent;
import vue.event.StepEvent;
import vue.event.VueEvent;

public class Controleur {

	public final static String VEGETAUX_PROPERTY_NAME = "simulation.vegetaux.nombre";
	public final static String ANIMAUX_PROPERTY_NAME = "simulation.animal.nombre";
	public final static String CHOSE_PROPERTY_NAME = "simulation.chose.nombre";

	AppWindow vue;
	Simulateur sim;// c'est le modèle
	FournisseurDeChose adapteurDeChose;

	public static void main(String[] args) {
		new Controleur().start();
	}

	/**
	 * Démarre l'application.
	 */
	private void start() {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					vue.montreVue();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	public Controleur() {
		super();
		int largeur = Terrain.getInstance().getXSize();
		int hauteur = Terrain.getInstance().getYSize();

		vue = new AppWindow(largeur, hauteur);

		// Le fournisseur de chose est l'alterego du simulateur pour l'affichage
		adapteurDeChose = new FournisseurDeChose() {

			private <I extends Chose> Collection<Affichable> adapteChose(
					Collection<I> choses) {
				Collection<Affichable> retVal = new ArrayList<>(choses.size());
				for (I chose : choses) {
					String name = chose.getName();
					String contenu = chose.toString();
					boolean estVivant = chose.estVivant();
					Coordonnee coordonnee = chose.getCoordonnees();
					XY xy = new XY(coordonnee.getX(), coordonnee.getY());
					float vie = chose instanceof Vivant ? ((Vivant) chose)
							.getVie() : 0;
					retVal.add(new Affichable(name, contenu, estVivant, xy, vie));
				}
				return retVal;
			}

			@Override
			public Collection<Affichable> getVegetaux() {
				return adapteChose(sim.getVegetaux().values());
			}

			@Override
			public String getNameForIndex(int index) {
				return sim.getNameForIndex(index);
			}

			@Override
			public Collection<Affichable> getLesChosesBasiques() {
				return adapteChose(sim.getLesChosesBasiques().values());
			}

			@Override
			public Collection<Affichable> getAnimaux() {
				return adapteChose(sim.getAnimaux().values());
			}

			@Override
			public Affichable getChose(String name) {
				Affichable retVal = null;
				Chose chose = sim.getChose(name);
				if (chose != null) {

					String contenu = chose.toString();
					boolean estVivant = chose instanceof Vivant ? ((Vivant) chose)
							.estVivant() : false;
					Coordonnee coordonnee = chose.getCoordonnees();
					XY xy = new XY(coordonnee.getX(), coordonnee.getY());
					float vie = chose instanceof Vivant ? ((Vivant) chose)
							.getVie() : 0f;
					retVal = new Affichable(name, contenu, estVivant, xy, vie);
				}
				return retVal;
			}
		};

		vue.addObserver((o, arg) -> {
			VueEvent event = (VueEvent) arg;
			if (event instanceof StepEvent) {
				stepSimulation();
			} else if (event instanceof StartEvent) {
				startSimulation();
			} else if (event instanceof InitEvent) {
				initSimulation();
			} else if (event instanceof StartEvent) {

			}
		});

		vue.setFournisseur(adapteurDeChose);
		vue.initialize();
		initSimulation();
	}

	/**
	 * Réinitialise la simulation. Remet la simulation dans l'état initiale
	 * prévu par les fichiers de propriétés.
	 */
	public void initSimulation() {
		// On crée un nouveau simulateur au besoin
		if (sim == null) {
			sim = new Simulateur();
		} else {
			sim.init();
		}
		// On configure le simulateur
		SimFactory.reset();
		configureSimulation();

		// on met la vue à jour
		vue.initSimulation();

		// On synchronise la durée de vie max des plantes
		Affichable.setVieMax(Vegetal.VIE_MAX);
	}

	/**
	 * Configure les paramètres de la simulation. Lecture du fichier de
	 * propriétés et inscription des propriétés dans la simulation.
	 */
	public void configureSimulation() {

		SimFactory fabrique = SimFactory.getInstance();
		sim.ajouteAnimaux(fabrique.getAll(ChosesPossibles.ANIMAL));
		sim.ajouteVegetaux(fabrique.getAll(ChosesPossibles.VEGETAL));
		sim.ajouteChoses(fabrique.getAll(ChosesPossibles.CHOSE));
	}

	/**
	 * Débute la simulation. Inactive tant que les threads ne sont pas utilisés
	 */
	private void startSimulation() {
		initSimulation();
		while (sim.resteToujoursUnVivant()) {
			sim.step();
		}
	}

	/**
	 * Avance la simulation d'un pas de temps
	 */
	private void stepSimulation() {
		if (sim == null) {
			initSimulation();
		}

		sim.step();
		sim.enleveMort();

		System.out.println("step " + sim.getStep() + "\n" + sim.toString());
		vue.rafraichitVue(sim.getStep());
	}
}
